<?php

namespace Largezhou\Admin\Form\Fields;

use Largezhou\Admin\Form\Field;
use Largezhou\Admin\Form\Form;

class Upload extends Field
{
    protected $value = [];
    protected $default = [];

    protected $props = [
        'multiple' => true,
        'limit'    => 0,
        'action'   => null,
        'accept'   => null,
    ];

    /**
     * 自定义上传目录.
     *
     * @var string
     */
    protected $dir = 'lz-uploads';

    /**
     * 自定义文件系统驱动配置名.
     *
     * @var null
     */
    protected $driver = null;

    /**
     * 判断上传是多图还是单图.
     *
     * @return bool
     */
    public function single()
    {
        return $this->limit() === 1;
    }

    /**
     * 当limit设置为1个时,自动设置multiple为false.
     *
     * @param int $limit
     *
     * @return $this
     */
    public function limit($limit = null)
    {
        if (func_num_args() === 0) {
            return $this->props('limit');
        }

        $this->props('limit', $limit);

        if ($limit === 1) {
            $this->multiple(false);
        }

        return $this;
    }

    /**
     * 设置允许上传的文件类型
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept
     *
     * @param $accept
     *
     * @return array|Upload|mixed
     */
    public function accept($accept)
    {
        return $this->props('accept', $accept);
    }

    public function variables(): array
    {
        // 可以自己设置不同的action路径,没有则默认
        if ($this->action() === null) {
            $this->action(full_request_path($this->form->isMode(Form::MODE_EDIT) ? route_path(-2) : route_path(-1)));
        }

        return parent::variables();
    }

    public function setValue($value)
    {
        $this->value = $this->handleValue($value);

        return $this;
    }

    public function getValue()
    {
        return empty($this->value) ? $this->getDefault() : $this->value;
    }

    public function setDefault($default)
    {
        $this->default = $this->handleValue($default);
        // 默认的图片,在前端移除时,不会删除文件
        $this->default = array_map(function ($v) {
            $v['retain'] = true;

            return $v;
        }, $this->default);

        return $this;
    }

    /**
     * 传来的是字符串,根据当前是否支持多选，把值处理成前端用的数组.
     *
     * @param $value
     *
     * @return array
     */
    protected function handleValue($value)
    {
        if ($this->single()) {
            $value = [$value];
            $value = array_filter($value);
        } else {
            $value = explode(',', $value);
            $value = array_filter($value);
        }

        return array_map(function ($v) {
            return [
                'name' => basename($v),
                'url'  => $v,
            ];
        }, $value);
    }

    public function prepare($value)
    {
        // 处理被标记为删除的文件
        $value = $this->handleRemove($value);

        // 处理前端传来的值
        $value = array_map(function ($v) {
            return $v['url'];
        }, $value);

        if ($this->single()) {
            return array_shift($value);
        } else {
            return implode(',', $value);
        }
    }

    /**
     * 处理被标记为删除的文件,并返回没有被标记为删除的文件.
     *
     * @param $files
     *
     * @return array
     */
    protected function handleRemove($files)
    {
        $removed = [];
        $value = [];

        foreach ($files as $file) {
            // 如果被标记为删除,且没有被标记为需要保留,则放到即将删除的数组中
            // 否则如果没有被标记为删除,则作为该字段的值
            if (array_get($file, 'removed') && !array_get($file, 'retain')) {
                $removed[] = $this->storage()->path($file['url']);
            } elseif (!array_get($file, 'removed')) {
                $value[] = $file;
            }
        }

        \File::delete($removed);

        return $value;
    }

    /**
     * 设置文件系统驱动配置名.
     *
     * @param string $driver
     *
     * @return $this
     */
    public function driver(string $driver)
    {
        $this->driver = $driver;

        return $this;
    }

    /**
     * 设置存储目录.
     *
     * @param string $dir
     *
     * @return $this
     */
    public function dir(string $dir)
    {
        $this->dir = $dir;

        return $this;
    }

    /**
     * 获取文件系统驱动.
     *
     * @return \Illuminate\Filesystem\FilesystemAdapter
     */
    protected function storage()
    {
        $driver = $this->driver ?: 'admin_upload_driver';

        return \Storage::drive($driver);
    }

    /**
     * 存储文件,并返回文件名和文件的相对路径.
     *
     * @return array
     */
    public function storeFile()
    {
        $file = \Request::file($this->name());
        $path = $this->storage()->putFile($this->dir, $file);

        return [
            'name' => $file->getClientOriginalName(),
            'url'  => $this->storage()->url($path),
        ];
    }
}
